package dingtalkLogHandler

import (
	"context"
	"fmt"
	"github.com/blinkbean/dingtalk"
	"log/slog"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

type Handler struct {
	attrs       []slog.Attr
	group       []string
	replaceAttr func(groups []string, attr slog.Attr) slog.Attr
	ding        *dingtalk.DingTalk
	ats         []string
}

func New(token, secret string, ats []string, replaceAttr func(groups []string, attr slog.Attr) slog.Attr) *Handler {
	ding := dingtalk.InitDingTalkWithSecret(token, secret, dingtalk.WithInitSendTimeout(2*time.Second))
	return &Handler{
		replaceAttr: replaceAttr,
		ding:        ding,
		ats:         ats,
	}
}

func (h *Handler) Enabled(_ context.Context, level slog.Level) bool {
	return level > slog.LevelInfo
}

func (h *Handler) Handle(_ context.Context, record slog.Record) error {
	//出错啦!!! 出错啦!!!! 出错啦!!!!
	//### 日志上下文
	//- **发生时间:** 24-10-31 15:27:14
	//- **日志位置:** test\main.go:92
	//---
	//- **id:** 123456
	//- **err:**
	//  - **err:** <font color="red">3 errors occurred:[{ "第一个错误" }, { "第二个错误" }, { "第三个错误" }]</font>
	//  - **traces:** risheng/test/main.go:90
	//- **request:**
	//  - **method:** POST
	//---
	//<mark>测试消息</mark>

	buf := &strings.Builder{}
	switch record.Level {
	case slog.LevelWarn:
		buf.WriteString("警告! 警告! 警告!\n")
	case slog.LevelError:
		buf.WriteString("出错啦!!! 出错啦!!!! 出错啦!!!!\n")
	default:
		buf.WriteString(fmt.Sprintf("收到一条日志, 日志级别为: %d\n", record.Level))
	}
	buf.WriteString("### 日志上下文\n")
	if !record.Time.IsZero() {
		// 输出时间
		buf.WriteString("- **发生时间:** ")
		buf.WriteString(record.Time.Format("06-01-02 15:04:05"))
		buf.WriteRune('\n')
	}
	if record.PC != 0 {
		// 输出日志位置
		fs := runtime.CallersFrames([]uintptr{record.PC})
		f, _ := fs.Next()
		if f.File != "" {
			dir, file := filepath.Split(f.File)
			buf.WriteString("- **日志位置:** ")
			buf.WriteString(filepath.Join(filepath.Base(dir), file))
			buf.WriteString(fmt.Sprintf(":%d\n", f.Line))
		}
	}
	buf.WriteString("---\n")
	var writerAttr = func(slog.Attr, int) {}
	writerAttr = func(attr slog.Attr, ident int) {
		if h.replaceAttr != nil {
			attr = h.replaceAttr(nil, attr)
		}
		// 先写标题
		buf.WriteString(fmt.Sprintf("%s- **%s:** ", strings.Repeat(" ", 2*ident), attr.Key))
		// 再写值
		if attr.Value.Kind() == slog.KindGroup {
			ident++
			buf.WriteRune('\n')
			for _, attr := range attr.Value.Group() {
				writerAttr(attr, ident)
			}
			return
		}
		template := "%v\n"
		if attr.Key == "err" {
			template = `<font color="red">%v</font>` + "\n"
		}
		buf.WriteString(fmt.Sprintf(template, attr.Value))
	}

	for _, attr := range h.attrs {
		writerAttr(attr, 0)
	}

	record.Attrs(func(attr slog.Attr) bool {
		writerAttr(attr, 0)
		return true
	})

	buf.WriteString("---\n")
	buf.WriteString(fmt.Sprintf(`<mark>%s</mark>`, record.Message))

	var title string
	switch record.Level {
	case slog.LevelWarn:
		title = "日晟系统发出了警告日志"
	case slog.LevelError:
		title = "日晟系统发出了错误日志"
	default:
		title = "日晟系统发出了日志"
	}
	err := h.ding.SendMarkDownMessage(title, buf.String(), dingtalk.WithAtMobiles(h.ats))
	return err
}

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
	newHandler := *h
	newHandler.attrs = append(newHandler.attrs, attrs...)
	return &newHandler
}

func (h *Handler) WithGroup(_ string) slog.Handler {
	return h
}
